#!/usr/bin/env python
#-*- coding:utf-8 -*-

__author__ = "Pierre Bienaime"
__author_email__ = "pbienaim@gmail.com"
__date__ = "2011-11-01"
__all__ = ["convert", "UnknownNode", "OtherError", "ProgrammerError", "Error_"]
# Inspired by sexp.py



#from HTMLParser import HTMLParser

#class MyHTMLParser(HTMLParser):
#    def handle_starttag(self, tag, attrs):
#        print "Encountered a start tag:", tag + " Attrs: " + repr(attrs) 
#    def handle_endtag(self, tag):
#        print "Encountered  an end tag:", tag
#    def handle_data(self, data):
#        print "Encountered   some data:", data
#        
#parser = MyHTMLParser()
#f = open("/tmp/bh1.html").read()
#parser.feed(f)


#import sys
#sys.exit(0)












# Currently is a copy-paste of sexp.py
# Will change soon

class Error_(Exception): pass
class UnknownNode(Error_): pass
class OtherError(Error_): pass
class ProgrammerError(OtherError): pass

import sys
import pynarcissus.jsparser as js
    
def o(n, i, c, handledattrs=[], ex=True, ev=True, scope="global"):
    attrs_ = {}
    for attr in handledattrs:
        attrs_[attr] = True
    subnodes_ = []
    had_error = False
    def check(attrs=[], optattrs=[], subnodes=0):
        if not (type(attrs) == list and type(optattrs) == list and
                type(subnodes) == int):
            raise ProgrammerError, "Wrong arguments to check(...)!"
        for attr in attrs: attrs_[attr] = True
        for attr in optattrs:
            if hasattr(n, attr): attrs_[attr] = True
        for i in xrange(subnodes):
            subnodes_.append(True)
    def props():
        if c["include_props"]:
            props_to_include = [x for x in ("lineno", "start", "end",
                    "readOnly") if hasattr(n, x)]
            if len(props_to_include) > 0:
                s = " (@"
                for prop in props_to_include:
                    s += " (%s %s)" % (prop.upper(), getattr(n, prop))
                return s +")"
        return ""
    try:
        check(attrs=["append", "count", "extend", "filename", "getSource",
                    "indentLevel", "index", "insert", "lineno", "pop",
                    "remove", "reverse", "sort", "tokenizer", "type", "type_"],
                    optattrs=["end", "start", "value"])

        if n.type == "ARRAY_INIT":
            check(subnodes=len(n))
            s = "[" + ",".join([str(eval_js(o(x,i,c, ex=ex, ev=ev))) for x in n]) + "]"
            return s

        elif n.type == "ASSIGN":
            check(subnodes=2)
            if getattr(n[0],"assignOp", None) is not None:
                print "FIXME ! ASSIGN op"
            vname = o(n[0], i, c, handledattrs=["assignOp"], ex=ex, ev=ev)
            if ev:
                v = eval_js(o(n[1], i, c, ex=ex, ev=ev))
            else:
                v = o(n[1], i, c, ex=ex, ev=ev)
            if ex:
                strip_and_store(v, vname, scope)
            return "{0} = {1}".format(vname, v)


        elif n.type == "BLOCK":
            check(subnodes=len(n))
#            if len(n) > 0:
#                return ("(BLOCK%s\n  " % props()) + i + \
#                        ("\n  "+i).join((o(x,i+"  ",c) for x in n)) + ")"
            if len(n) > 0:
                return i + (";\n"+i).join((o(x,i+"  ",c, ex=ex, ev=ev) for x in n)) + ";"
            return ""

        elif n.type in ("BREAK", "CONTINUE"):
            check(attrs=["target"], optattrs=["label"])
            if hasattr(n,"label"):
                return "(%s%s %s)" % (n.value.upper(), props(), n.label)
            return "(%s%s)" % (n.value.upper(), props())

        elif n.type == "CALL":
            check(subnodes=2)
#            return "(CALL%s %s%s)" % (props(), o(n[0], i, c), o(n[1], i, c))
            return eval_js_for_call(o(n[0], i, c, ex=ex, ev=ev), o(n[1], i, c, ex=ex, ev=ev))

        elif n.type == "CASE":
            check(attrs=["caseLabel","statements"])
            return "(CASE%s %s %s)" % (props(), o(n.caseLabel,i,c, ex=ex, ev=ev),
                    o(n.statements,i,c, ex=ex, ev=ev))

        elif n.type == "CATCH":
            check(attrs=["block","guard","varName"])
            if n.guard is not None:
                return "(GUARDED-CATCH%s %s %s %s)" % (props(), n.varName,
                        o(n.guard,i,c, ex=ex, ev=ev), o(n.block,i,c, ex=ex, ev=ev))
            return "catch({0}) {{\n{1}\n}}".format(n.varName, o(n.block,i,c, ex=ex, ev=ev))

        elif n.type == "COMMA":
            check(subnodes=2)
            return "(COMMA%s %s %s)" % (props(), o(n[0],i,c, ex=ex, ev=ev), o(n[1],i,c, ex=ex, ev=ev))

        elif n.type == "DEBUGGER":
            return "(DEBUGGER%s)" % props()

        elif n.type == "DEFAULT":
            check(attrs=["statements"])
            return "(DEFAULT%s %s)" % (props(), o(n.statements,i,c, ex=ex, ev=ev))

        elif n.type in ("DELETE", "TYPEOF", "NEW", "UNARY_MINUS", "NOT",
                "VOID", "BITWISE_NOT", "UNARY_PLUS"):
            check(subnodes=1)
#            return "(%s%s %s)" % (n.type.replace('_', '-'), props(),
#                    o(n[0],i,c))
            if n.type == "UNARY_PLUS":
                return "+ " + o(n[0],i,c, ex=ex, ev=ev)
            if n.type == "UNARY_MINUS":
                return "- " + o(n[0],i,c, ex=ex, ev=ev)
            elif n.type == "NEW":
                return "new " + o(n[0],i,c, ex=ex, ev=ev)
            else:
                return n.type + " " + o(n[0],i,c, ex=ex, ev=ev)

        elif n.type == "DO":
            check(attrs=["body", "condition", "isLoop"])
            assert n.isLoop
            return "(DO-WHILE%s %s %s)" % (props(), o(n.condition,i,c, ex=ex, ev=ev),
                    o(n.body,i,c, ex=ex, ev=ev))

        elif n.type == "DOT":
            check(subnodes=2)
#            return "(ATTRIBUTE%s %s %s)" % (props(), o(n[0],i,c), o(n[1],i,c))
            return o(n[0],i,c, ex=ex, ev=ev) + "." +  o(n[1],i,c, ex=ex, ev=ev)

        elif n.type == "FUNCTION":
            check(attrs=["functionForm","params","body"],
                    optattrs=["name"])
            if n.functionForm == 0:
                return "(DEF-FUNCTION%s (%s%s) %s)" % (props(), n.name,
                        "".join(" " + param for param in n.params),
                        o(n.body,i,c, ex=ex, ev=ev))
            else:
                return "(FUNCTION%s (%s) %s)" % (props(), " ".join(n.params),
                        o(n.body,i,c, ex=ex, ev=ev))

        elif n.type == "FOR":
            check(attrs=["body","setup","condition","update","isLoop"])
            assert n.isLoop
            if n.setup is not None: setup = o(n.setup,i,c, ex=ex, ev=ev)
            else: setup = "(BLOCK)"
            if n.condition is not None: condition = o(n.condition,i,c, ex=ex, ev=ev)
            else: condition = "(BLOCK)"
            if n.update is not None: update = o(n.update,i,c, ex=ex, ev=ev)
            else: update = "(BLOCK)"
            if n.body is not None: body = o(n.body,i+"  ",c, ex=ex, ev=ev)
            else: body = "(BLOCK)"
            return "(FOR%s %s %s %s\n  %s%s)" % (props(), setup, condition,
                    update, i, body)

        elif n.type == "FOR_IN":
            check(attrs=["body","iterator","object","isLoop","varDecl"])
            assert n.isLoop
            s = "(FOR-IN"
            if n.varDecl:
                assert n.varDecl.type == "VAR"
                assert len(n.varDecl) == 1
                assert n.varDecl[0].type == "IDENTIFIER"
                assert n.varDecl[0].value == n.iterator.value
                s += "-VAR"
            return s + "%s %s %s %s)" % (props(), o(n.iterator,i,c, ex=ex, ev=ev),
                    o(n.object,i,c, ex=ex, ev=ev), o(n.body,i,c, ex=ex, ev=ev))

        elif n.type == "GROUP":
            check(subnodes=1)
            return o(n[0],i,c, ex=ex, ev=ev)

        elif n.type == "HOOK":
            check(subnodes=3)
            return "(TERNARY%s %s %s %s)" % (props(),o(n[0],i,c, ex=ex, ev=ev),o(n[1],i,c, ex=ex, ev=ev),
                    o(n[2],i,c, ex=ex, ev=ev))

        elif n.type == "IDENTIFIER":
            check(optattrs=["initializer","name","readOnly"])
#            if getattr(n,"readOnly",False): assert hasattr(n,"initializer")
            if hasattr(n,"name"): assert n.name == n.value
            if hasattr(n,"initializer"):
                vname = n.value
                v =  eval_js(o(n.initializer, i, c, ex=ex, ev=ev))
                if ex:
                    strip_and_store(v, vname, scope)
                return "{0} = {1}".format(vname, v)
            return str(n.value)

        elif n.type == "IF":
            check(attrs=["condition","thenPart","elsePart"])
            ex = True
            if o(n.condition,i,c, ex=ex, ev=ev) != "0":
                ex = False
            if n.elsePart:
#               #if-else
                return "if ( {0} )\n{{\n{1}\n}} else {{\n{2}\n}}".format(o(n.condition,i,c, ex=ex, ev=False), o(n.thenPart,i+"  ",c, ex=ex, ev=ev), o(n.thenPart, i+"  ",c, ex=ex))
            return "if ( {0} )\n{{\n{1}\n}}".format(o(n.condition,i,c, ex=ex, ev=False), o(n.thenPart, i+"  ",c, ex=ex, ev=ev))


        elif n.type in ("INCREMENT", "DECREMENT"):
            check(optattrs=["postfix"], subnodes=1)
            if getattr(n, "postfix", False):
                return "(POST-%s%s %s)" % (n.type, props(), o(n[0], i, c, ex=ex, ev=ev))
            return "(%s%s %s)" % (n.type, props(), o(n[0], i, c, ex=ex, ev=ev))

        elif n.type == "INDEX":
            check(subnodes=2)
#            return "(ARRAY-INDEX%s %s %s)" % (props(), o(n[0],i,c), o(n[1],i,c))
            vname = o(n[0],i,c, ex=ex, ev=ev)
            index = eval_js(o(n[1],i,c, ex=ex, ev=ev))
            if type(index) == type(1):
                return eval_js("{0}[{1}]".format(vname, index))
            return "{0}.{1}".format(vname, index.strip("'"))
            
        elif n.type == "LABEL":
            check(attrs=["label","statement"])
            return "(LABELED-STATEMENT%s %s\n  %s%s)" % (props(), n.label, i,
                    o(n.statement, i+"  ", c, ex=ex, ev=ev))

        elif n.type == "LIST":
            check(subnodes=len(n))
            return ''.join((str(o(x, i, c, ex=ex, ev=ev)) + ',' for x in n))[:-1]

        elif n.type == "NEW_WITH_ARGS":
            check(subnodes=2)
#            return "(%s%s %s %s)" % (n.value.upper(), props(), o(n[0],i,c),
#                    o(n[1],i,c))
            obj = o(n[0],i,c, ex=ex, ev=ev)
            arg = o(n[1],i,c, ex=ex, ev=ev)
            if obj == "Object":
                return arg
            return "new {0}({1})".format(obj, arg) 

        elif n.type in ("NUMBER", "TRUE", "FALSE", "THIS", "NULL"):
            return str(n.value).upper()

        elif n.type == "OBJECT_INIT":
            check(subnodes=len(n))
            if len(n) > 0:
                return ("(OBJECT_INIT%s\n  " % props() + i +
                        ("\n  "+i).join(o(x,i+"  ",c, ex=ex, ev=ev) for x in n) + ")")
            return "(OBJECT-INIT%s)" % props()

        elif n.type in ("PLUS", "LT", "EQ", "AND", "OR", "MINUS", "MUL", "LE",
                "NE", "STRICT_EQ", "DIV", "GE", "INSTANCEOF", "IN", "GT",
                "BITWISE_OR", "BITWISE_AND", "BITWISE_XOR", "STRICT_NE", "LSH",
                "RSH", "URSH", "MOD"):
            check(subnodes=2)
            if ev:
                return eval_js("{0} {1} {2}".format(o(n[0],i,c, ex=ex, ev=ev), type_dict[n.type], o(n[1],i,c, ex=ex, ev=ev)))
            else:
                return "{0} {1} {2}".format(o(n[0],i,c, ex=ex, ev=ev), type_dict[n.type], o(n[1],i,c, ex=ex, ev=ev))

        elif n.type == "PROPERTY_INIT":
            check(subnodes=2)
            return "(PROPERTY%s %s %s)" % (props(), o(n[0],i,c, ex=ex, ev=ev), o(n[1],i,c, ex=ex, ev=ev))

        elif n.type == "REGEXP":
            return "(REGEXP%s %r %r)" % (props(), n.value["regexp"],
                    n.value["modifiers"])

        elif n.type == "RETURN":
            if type(n.value) == str:
                return "(RETURN%s %r)" % (props(), n.value)
            return "(RETURN%s %s)" % (props(), o(n.value, i, c, ex=ex, ev=ev))

        elif n.type == "SCRIPT":
            check(attrs=["funDecls","varDecls"], subnodes=len(n))
##            sys.stderr.write("WARNING: skipping funDecls and varDecls\n")
#            if len(n) > 0:
#                return ("(SCRIPT%s\n  " % props() + i +
#                        ("\n  "+i).join((o(x,i+"  ",c) for x in n)) + ")")
#            return "(SCRIPT%s)" % props()
            if len(n) > 0:
                return (i + (";\n"+i).join((o(x,i+"  ",c, ex=ex, ev=ev) for x in n)))
            return "(SCRIPT%s)" % props()

        elif n.type == "SEMICOLON":
            check(attrs=["expression"])
            if not n.expression: return "(BLOCK)"
            return o(n.expression, i, c, ex=ex, ev=ev)

        elif n.type == "STRING":
            return repr(n.value)

        elif n.type == "SWITCH":
            check(attrs=["cases", "defaultIndex", "discriminant"])
            assert (n.defaultIndex == -1 or
                    n.cases[n.defaultIndex].type == "DEFAULT")
            return "(SWITCH%s %s\n  %s%s)" % (props(), o(n.discriminant,i,c, ex=ex, ev=ev), i,
                    ("\n  "+i).join(o(x,i+"  ",c, ex=ex, ev=ev) for x in n.cases))

        elif n.type == "THROW":
            check(attrs=["exception"])
            return "(THROW%s %s)" % (props(), o(n.exception,i,c, ex=ex, ev=ev))

        elif n.type == "TRY":
            check(attrs=["catchClauses","tryBlock"], optattrs=["finallyBlock"])
            if hasattr(n,"finallyBlock"):
                return "(TRY-FINALLY%s\n  " % props() + i + ("\n  "+i).join(
                        [o(n.tryBlock,i+"  ",c, ex=ex, ev=ev)] + [o(x,i+"  ",c, ex=ex, ev=ev)
                        for x in n.catchClauses] + \
                        [o(n.finallyBlock,i+"  ",c, ex=ex, ev=ev)]) + ")"
            return "try {\n" + ("\n  "+i).join([o(n.tryBlock,i+"  ",c, ex=ex, ev=ev)]) +"\n}\n" + "".join([o(x,i+"  ",c, ex=ex, ev=ev) for x in n.catchClauses])

        elif n.type in ("VAR", "CONST"):
            check(subnodes=len(n))
#            return ("(%s%s " % (n.value.upper(), props())) + " ".join((o(x,i,c)
#                    for x in n)) + ")"
            v = n.value.lower()
            return ";\n".join((v + " " + o(x,i,c, ex=ex, ev=ev) for x in n))

        elif n.type == "WITH":
            check(attrs=["body", "object"])
            sys.stderr.write("WARNING: A bad person wrote the code being "
                    "parsed. Don't use 'with'!\n")
            return "(WITH%s %s %s)" % (props(), o(n.object,i,c, ex=ex, ev=ev), o(n.body,i,c, ex=ex, ev=ev))

        elif n.type == "WHILE":
            check(attrs=["condition","body","isLoop"])
            assert n.isLoop
            return "(WHILE%s %s\n  %s%s)" % (props(), o(n.condition,i,c, ex=ex, ev=ev), i,
                    o(n.body, i+"  ", c, ex=ex, ev=ev))

        else:
            raise UnknownNode, "Unknown type %s" % n.type
    except Exception, e:
        had_error = True
        raise OtherError("%s\nException in node %s on line %s" % (e, n.type,
                getattr(n, "lineno", None)))
    finally:
        if not had_error:
            realkeys = [x for x in dir(n) if x[:2] != "__"]
            for key in realkeys:
                if key not in attrs_:
                    raise ProgrammerError, "key '%s' unchecked on node %s!" % (
                            key, n.type)
            if len(realkeys) != len(attrs_):
                for key in attrs_:
                    if key not in realkeys:
                        raise ProgrammerError, ("key '%s' checked "
                                "unnecessarily on node %s!" % (key, n.type))
            if len(subnodes_) != len(n):
                raise ProgrammerError, ("%d subnodes out of %d checked on node "
                        "%s" % (len(subnodes_), len(n), n.type))

def convert(parsetree, include_props=False):
    """Takes a PyNarcissus parse tree and returns a string of S-expressions

    Args:
        parsetree: the PyNarcissus parse tree
        include_props: if true, the s-expressions have additional information
            included via @ attribute expressions, such as line-number.
    Returns:
        string
    Raises:
        UnknownNode: if a node hasn't been properly accounted for in the
            conversion
        ProgrammerError: if the conversion routine wasn't written with the best
            understanding of the parse tree
        OtherError: if some other error happened we didn't understand.
    """

    return o(parsetree, "", {"include_props": include_props}) + ";\n"


import traceback

def eval_js(s, convert=True):
    try:
        e = eval(s, glob, local)
        if convert:
            return type_converter(e)
        return e
    except:
        return s
        
def eval_js_for_call(f, arg):
    if ( f in glob and glob[f] in glob) or (f in local and local[f] in local):
        f = eval_js(f, convert=False)
    arg = eval_js(arg)
    if f == "eval":
        if arg[0] in ["'",'"'] and arg[-1] in ["'",'"']:
            return arg[1:-1].replace("\\'","'").replace('\\"','"').replace('\\\\','\\')
        else:
            return "{0}({1})".format(f,arg)
    try:
        e = eval("{0}(*({1}))".format(f,arg), glob, local)
        return type_converter(e)
    except:
        try:
            e = eval("{0}({1})".format(f,arg), glob, local)
            return type_converter(e)
        except:
            pass
    return "{0}({1})".format(f,arg)

def type_converter(e):
    try:
        e = float(e)
        if e.is_integer():
            e = int(e)
    except (ValueError, OtherError):
        e = repr(e)
    return e
    
def strip_and_store(v, vname, scope):
    if type(v) == type("") and len(v) > 1 and v[0] in ["'",'"'] and v[-1] in ["'",'"']:
        v = v[1:-1]
    if scope == "global":
        glob[vname] = v
    elif scope == "local":
        local[vname] = v
        
type_dict = {
    "PLUS":"+",
    "LT":"<",
    "EQ":"==",
    "AND":"&&",
    "OR":"||",
    "MINUS":"-",
    "MUL":"*",
    "LE":"<=",
    "NE":"!=",
    "STRICT_EQ":"===",
    "DIV":"/",
    "GE":">=",
    "INSTANCEOF":"instanceof",
    "IN":"in",
    "GT":">",
    "BITWISE_OR":"|",
    "BITWISE_AND":"&",
    "BITWISE_XOR":"^",
    "STRICT_NE":"!==",
    "LSH":"<<",
    "RSH":">>",
    "URSH":">>>",
    "MOD":"%"
}

class String:
    @staticmethod
    def fromCharCode(*char):
        r = ""
        for c in char:
            r += chr(c)
        return r

def unescape(s):
    is_printable = True
    i = 0
    l = len(s)
    r = ""
    while i < l:
        if s[i] == "%" and i+2 <= l:
            if s[i+1] == "u" and i+5 <= l:
                try:
                    c = int(s[i+2:i+6], 16)
                except ValueError:
                    r += s[i:i+2]
                    i += 2
                    continue
                if c >= 32 and c <= 126:
                    r += chr(c)
                else:
                    r += s[i:i+6]
                    is_printable=False
                i += 6
            else:
                try:
                    c = int(s[i+1:i+3], 16)
                except ValueError:
                    r += s[i]
                    i += 1
                    continue
                if c >= 32 and c <= 126:
                    r += chr(c)
                else:
                    r += s[i:i+3]
                    is_printable = False
                i += 3
        else:
            r += s[i]
            i += 1
    if is_printable:
        return r
    return 'unescape("{0}")'.format(r)
                
glob = {}
# Disable __builtins__ to use eval() safely
glob["__builtins__"] = None
# Custom functions
glob["String"] = String
glob["eval"] = None
glob["unescape"] = unescape
local = {}



if __name__ == "__main__":
#    print unescape("%46%33%69%4D%68%6E%32%6C%72%65%66%6B%65%59%30%35%36%30%5A%6C%4B%41%35%43%6F%72%71%73%38%76%68%66%58%74%71%63%61%70%4C%67%46%69%37%55%52%37%45%35%74%6A%63%6C%32%69%43%33%32%69%4A%68%33%73%6A%6A%74%30%72%6E%56%51%52%65%45%78%78%32%34%4D%6F%64%52%73%5A%4B%65%7A%6C%72%7A%67%41%57%39%41%57%31%61%69%70%79%39%69%69%38%59%5A%37%47%31%64%38%38%39%34%69%38%37%68%35%32%45%6B%35%4B%36%56%54%76%58%37%36%6A%39%59%65%72%58%47%66%6F%69%79%39%4E%55%35%64%71%77%39%45%6C%58%59%6C%57%4D%69%45%6A%71%66%70%43%70%57%52%31%32%57%65%34%61%42%64%45%4B%64%58%4F%5A%4A%55%6D%35%4E%44%42%39%77%70%65%31%44%72%78%37%7A%45%37%7A%33%64%36%6A%35%37%33%6C%33%37%72%6A%50%55%6F%63%39%4B%67%62%61%64%70%63%74%75%49%61%63%57%67%70%39%77%78%37%64%32%33%33%39%34%31%35%6D%6C%6A%47%46%68%55%44%6D%51%62%7A%53%63%35%76%61%4F%31%54%61%55%78%73%35%62%54%7A%51%52%4B%48%56%32%6F%6C%79%35%64%4E%6A%6E%46%68%34%47%30%35%65%30%31%69%32%4C%48%32%79%53%45%49%42%65%43%38%68%72%77%66%38%63%37%74%31%31%4E%67%39%6E%69%77%4B%6C%74%63%37%36%79%39%53%70%61%75%4D%68%38%36%77%64%6E%46%4F%54%46%7A%51%53%37%39%37%6D%32%77%71%6C%6A%36%67%79%5A%6C%72%6D%50%4B%4B%64%4C%70%69%51%4F%6F%6E%41%50%4A%54%6B%30%6E%31%52%72%76%4D%41%75%0A%6C%34%7C%38%7B%67%6D%36%44%3C%51%4A%5D%7B%4E%32%66%58%76%36%40%20%60%4D%37%36%7C%52%60%17%1C%3B%37%36%7C%40%71%73%31%0E%1F%34%7C%16%72%36%7C%10%01%04%01%01%01%01%01%01%01%01%01%27%34%34%7C%30%60%5B%3B%0A%6C%34%7C%66%34%3A%64%6E%38%4D%64%57%4B%48%34%3F%5D%57%38%66%33%7A%7B%4D%37%36%7C%7A%77%68%77%3B%37%36%7C%5C%7A%02%02%21%62%37%7C%64%66%02%06%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%3B%37%36%7C%7F%66%7F%51%21%62%37%7C%7F%66%7F%77%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%3B%37%36%7C%4C%02%36%2E%21%62%37%7C%79%02%30%7F%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%3B%37%36%7C%76%11%57%11%21%62%37%7C%70%20%72%6F%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%3B%37%36%7C%33%45%08%23%21%62%37%7C%5A%34%10%66%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%0D%69%35%7C%3B%37%36%7C%75%6A%41%33%21%62%37%7C%3A%61%62%70%0E%6B%34%7C")
#    import sys
#    sys.exit(0)

    try:
        include_props = (sys.argv[1] == "--props")
    except:
        include_props = False

    sys.stdout.write(convert(js.parse(sys.stdin.read()), include_props))
